<?php
/**
 * Created by PhpStorm.
 * User: 小灰灰
 * Date: 2023-03-13
 * Time: 10:27:47
 * Info: 上传
 */

namespace app\admin\controller;

use think\facade\Request;
use think\facade\View;
use think\Image;
use app\common\model\UploadFile as UploadFileModel;
use app\common\model\UploadGroup as UploadGroupModel;
use think\Validate;

class Upload extends Base
{

    public function __construct()
    {
        header('Access-Control-Allow-Origin: *');
        $this->admin_id    = cmf_get_admin_id() ?? 0;
        $this->user_id     = session('user_id') ?? 0;
        $this->upload_mode = get_config('upload_mode');
    }

    /**
     * 上传附件
     * @return void
     */
    public function index($editorType = '')
    {
        $save_path = Request::param('save_path', 'images');
        $groupId   = input('group_id', '0');
        $group_id  = $groupId == '-1' ? 0 : $groupId;

        if ($editorType == 'ueditor') {
            $save_path = Request::param('action');
        }

        try {
            $file = request()->file('file');
        } catch (\think\Exception $e) {
            $this->error($this->_languageChange($e->getMessage()));
        }

        try {
            $getMime = $file->getMime();

            if (strstr($getMime, 'image')) {
                try {
                    validate([
                        'imgFile' => [
                            'fileSize' => intval(get_config('upload_maxsize')) * 1000,
                            'fileExt'  => get_config('upload_types_image')
                        ]
                    ])->check(['file' => $file]);
                } catch (\think\exception\ValidateException $e) {
                    $this->error($e->getMessage());
                }
            } else {
                try {
                    validate([
                        'imgFile' => [
                            'fileSize' => intval(get_config('upload_maxsize')) * 1000,
                            'fileExt'  => get_config('upload_types_file')
                        ]
                    ])->check(['file' => $file]);
                } catch (\think\exception\ValidateException $e) {
                    $this->error($e->getMessage());
                }
            }
            //保存附件到本地服务器
            try {
                $saveName = \think\facade\Filesystem::disk('public')->putFile($save_path, $file);
            } catch (\think\exception\ValidateException $e) {
                $this->error($e->getMessage());
            }
            $saveName = str_replace("\\", "/", $saveName);
            $fileName = explode('/', $saveName);
            $fileName = ! empty(end($fileName)) ? end($fileName) : '';
            $saveUrl  = (\think\facade\Filesystem::disk('public')->url($saveName));

            if (strstr($getMime, 'image')) {
                //水印-图片
                $this->add_water($saveUrl);
            }
            //写入数据库
            $this->_att_write($file, $saveUrl, $fileName, $save_path, $group_id);

            if ($editorType == 'ueditor') {
                //ueditor编辑器上传输出
                return json(['state' => 'SUCCESS', 'url' => $saveUrl]);
            } else {
                //其他输出
                return json([
                    'code' => 200,
                    'msg'  => '上传成功',
                    'name' => $fileName,
                    'url'  => $saveUrl
                ]);
            }

        } catch (\think\Exception $e) {
            $this->error($e->getMessage());
        }

    }

    /**
     * base64类型图片上传（涂鸦上传）
     * @return void
     */
    public function upload_base64()
    {
        $base64_image_content = Request::param('file');
        if (empty($base64_image_content)) {
            return false;
        }
        //合成图片的base64编码成
        $base64_image_content = "data:image/png;base64,{$base64_image_content}";
        //匹配出图片的信息
        $match = preg_match('/^(data:\s*image\/(\w+);base64,)/', $base64_image_content, $result);
        if ( ! $match) {
            return false;
        }

        //解码图片内容
        $base64_image = str_replace($result[1], '', $base64_image_content);
        $file_content = base64_decode($base64_image);
        $file_type    = $result[2];

        $localDir = config('filesystem.disks.public.url');
        //如果没指定目录,则保存在当前目录下
        $pathTime = date('Ymd');
        $filePath = $localDir."/crawl/".$pathTime."/";
        $path     = public_path().$filePath;
        if ( ! is_dir($path)) {
            @mkdir($path, 0777, true);
        }
        $file_name = time().".{$file_type}";

        $new_file = $path.$file_name;

        if (file_exists($new_file)) {
            //有同名文件删除
            @unlink($new_file);
        }
        if (file_put_contents($new_file, $file_content)) {
            return json(['state' => 'SUCCESS', 'url' => $filePath.$file_name]);
        }

        return false;
    }

    //添加水印
    private function add_water($fileName)
    {
        //获取水印配置
        if (get_config('watermark_enable')) {
            $waterpic = "./static/water/".get_config('watermark_name');
            if ( ! is_file($waterpic)) {
                return;
            }
            $pic_url = '.'.$fileName;
            $image   = Image::open($pic_url);
            $image->water($waterpic, get_config('watermark_position'), get_config('watermark_touming'))->save($pic_url);
        }
    }

    //写入数据库
    private function _att_write($file, $fileName, $picName, $save_path, $group_id)
    {
        $img_width  = 0;
        $img_height = 0;
        $is_image   = 1;

        if ($save_path == 'images') {
            //去除.gif格式的丿打开
            if (strstr($fileName, '.gif')) {
                $extension = 'gif';
            } else {
                $fileinfo   = Image::open('.'.$fileName);
                $extension  = $fileinfo->type();
                $img_width  = $fileinfo->width();
                $img_height = $fileinfo->height();
            }
        } else {
            $is_image  = 0;
            $extension = $this->get_file_ext($fileName);
        }
        $operation_id = $this->admin_id ?? $this->user_id;
        $role_id      = $this->admin_id ?? 0;

        $data = [
            'storage'    => $this->upload_mode,
            'group_id'   => $group_id,
            'file_url'   => $fileName,
            'file_name'  => $picName,
            'file_size'  => $file->getSize(),
            'file_type'  => $save_path,
            'extension'  => $extension,
            'sha1'       => $file->hash('sha1'),
            'is_image'   => $is_image,
            'img_width'  => $img_width,
            'img_height' => $img_height,
            'upload_ip'  => get_client_ip(),
            'user_id'    => $operation_id,
            'role_id'    => $role_id
        ];
        UploadFileModel::create($data);
    }

    /**
     * 错误英文转为中文
     */
    private function _languageChange($msg)
    {
        $data = [
            // 上传错误信息
            'unknown upload error'                       => '未知上传错误！',
            'file write error'                           => '文件写入失败！',
            'upload temp dir not found'                  => '找不到临时文件夹！',
            'no file to uploaded'                        => '没有文件被上传！',
            'only the portion of file is uploaded'       => '文件只有部分被上传！',
            'upload File size exceeds the maximum value' => '上传文件大小超过了最大值！',
            'upload write error'                         => '文件上传保存错误！',
        ];

        return $data[$msg] ?? $msg;
    }

    //获取上传文件后缀
    private function get_file_ext($file_name)
    {
        $temp_arr = explode(".", $file_name);
        $file_ext = array_pop($temp_arr);
        $file_ext = trim($file_ext);
        $file_ext = strtolower($file_ext);

        return $file_ext;
    }

    /**
     * uEditorPlus上传服务配置项输出
     * @return array
     */
    public function ueditor_config()
    {
        $config = $this->configUeditor();

        $action = Request::param('action');
        switch ($action) {
            //图片上传
            case 'images':
                $editorType = "ueditor";

                return $this->index($editorType);

            // 图片列表
            case 'listImage':
                $start = Request::param('start', 0);
                $size  = Request::param('size', 20);

                $list = $this->actionList($config, 'listImage', $start, $size);

                return $list;

            // 上传视频
            case 'video':
                $result = ['state' => 'ERROR', 'url' => ''];

                return json($result);

            //文件上传
            case 'files':
                $editorType = "ueditor";

                return $this->index($editorType);

            //文件列表
            case 'listFile':
                $start = Request::param('start', 0);
                $size  = Request::param('size', 20);

                $list = $this->actionList($config, 'listFile', $start, $size);

                return $list;

            // 涂鸦上传
            case 'crawl':
                $editorType = "ueditor";

                return $this->upload_base64($editorType, true);
            default:
                return json($config);
        }

    }

    /*
     * ueditor列表附件
     */
    private function actionList($config, $type = 'listImage', $start = 0, $size = 20)
    {
        switch ($type) {
            case 'listImage':
                $allowFiles = $config['imageManagerAllowFiles'];
                $path       = $config['imageManagerListPath'].'images/';
                break;
            case "listFile":
                $allowFiles = $config['fileManagerAllowFiles'];
                $path       = $config['fileManagerListPath'].'files/';
                break;
        }
        $allowFiles = substr(str_replace(".", "|", join("", $allowFiles)), 1);
        /* 获取参数 */
        $end  = $start + $size;
        $path = root_path().str_replace("./", "", $path);
        /* 获取文件列表，有问题，默认显示0 */
        $files = $this->getfiles($path, $allowFiles);

        if ( ! empty($files) && ! count($files)) {
            return json_encode([
                "state" => "no match file",
                "list"  => [],
                "start" => $start,
                "total" => count($files)
            ]);
        }
        /* 获取指定范围的列表 */
        $len = count($files);
        for ($i = min($end, $len) - 1, $list = []; $i < $len && $i >= 0 && $i >= $start; $i--) {
            $list[] = $files[$i];
        }
        $list = arrayOrder($list, 'mtime');

        /* 返回数据 */
        $result = [
            "state" => "SUCCESS",
            "list"  => $list,
            "start" => $start,
            "total" => count($files)
        ];

        return json($result);
    }

    private function configUeditor()
    {
        $stroage_local_dir = "uploads";

        $imageAllowFiles = get_config('upload_types_image');
        $imageAllowFiles = explode(',', $imageAllowFiles);
        foreach ($imageAllowFiles as $key => $v) {
            $imageAllowFiles[$key] = ".".$v;
        }
        $fileAllowFiles = get_config('upload_types_file');
        $fileAllowFiles = explode(',', $fileAllowFiles);
        foreach ($fileAllowFiles as $key => $v) {
            $fileAllowFiles[$key] = ".".$v;
        }

        $upload_maxsize = intval(get_config('upload_maxsize')) * 1000;

        $config = [
            // 上传图片配置项
            "imageActionName"         => "images",
            "imageFieldName"          => "file",
            "imageMaxSize"            => $upload_maxsize,
            "imageAllowFiles"         => $imageAllowFiles,
            "imageCompressEnable"     => true,
            "imageCompressBorder"     => 5000,
            "imageInsertAlign"        => "none",
            "imageUrlPrefix"          => "",

            // 涂鸦图片上传配置项
            "scrawlActionName"        => "crawl",
            "scrawlFieldName"         => "file",
            "scrawlMaxSize"           => $upload_maxsize,
            "scrawlUrlPrefix"         => "",
            "scrawlInsertAlign"       => "none",

            // 截图工具上传
            "snapscreenActionName"    => "snap",
            "snapscreenUrlPrefix"     => "",
            "snapscreenInsertAlign"   => "none",

            // 抓取
            "catcherLocalDomain"      => ["127.0.0.1", "localhost"],
            "catcherActionName"       => "catch",
            "catcherFieldName"        => "source",
            "catcherUrlPrefix"        => "",
            "catcherMaxSize"          => $upload_maxsize,
            "catcherAllowFiles"       => $imageAllowFiles,

            // 上传视频配置
            "videoActionName"         => "video",
            "videoFieldName"          => "file",
            "videoUrlPrefix"          => "",
            "videoMaxSize"            => $upload_maxsize,
            "videoAllowFiles"         => ['.mp4'],

            // 上传文件配置
            "fileActionName"          => "files",
            "fileFieldName"           => "file",
            "fileUrlPrefix"           => "",
            "fileMaxSize"             => $upload_maxsize,
            "fileAllowFiles"          => $fileAllowFiles,

            // 列出图片
            "imageManagerActionName"  => "listImage",   /* 执行图片管理的action名称 */
            "imageManagerListPath"    => "./public/".$stroage_local_dir."/",    /* 指定要列出图片的目录 */
            "imageManagerListSize"    => 20,    /* 每次列出文件数量 */
            "imageManagerUrlPrefix"   => "",    /* 图片访问路径前缀 */
            "imageManagerInsertAlign" => "none",    /* 插入的图片浮动方式 */
            "imageManagerAllowFiles"  => $imageAllowFiles, /* 列出的文件类型 */

            // 列出指定目录下的文件
            "fileManagerActionName"   => "listFile",
            "fileManagerListPath"     => "./public/".$stroage_local_dir."/",
            "fileManagerUrlPrefix"    => "",
            "fileManagerListSize"     => 20,
            "fileManagerAllowFiles"   => $fileAllowFiles

        ];

        return $config;
    }

    /**
     * 遍历获取目录下的指定类型的文件
     *
     * @param       $path
     * @param array $files
     *
     * @return array
     */
    function getfiles($path, $allowFiles, &$files = array())
    {
        if ( ! is_dir($path)) {
            return null;
        }

        if (substr($path, strlen($path) - 1) != '/') {
            $path .= '/';
        }

        $handle = opendir($path);
        while (false !== ($file = readdir($handle))) {
            if ($file != '.' && $file != '..') {
                $path2 = $path.$file;
                if (is_dir($path2)) {
                    $path2 = $path2.'/';
                    $this->getfiles($path2, $allowFiles, $files);
                } else {
                    if (preg_match("/\.(".$allowFiles.")$/i", $file)) {
                        $files[] = array(
                            'url'   => str_replace("./public//", "/",
                                substr($path2, strlen($_SERVER['DOCUMENT_ROOT']))),
                            'mtime' => filemtime($path2)
                        );
                    }
                }
            }
        }

        $files = arrayOrder($files, 'mtime');

        return $files;
    }

}
